/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : mp3play.c
  * @brief          : mp3 play function
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/

#include "main.h"
#include "fatfs.h"

#include <stdio.h>
#include <string.h>
#include "mp3dec.h"

extern I2S_HandleTypeDef hi2s2;
extern DMA_HandleTypeDef hdma_spi2_tx;
extern SD_HandleTypeDef hsd;
extern UART_HandleTypeDef huart1;


// MP3
#define READBUF_SIZE 4096*2     // Value must min be 2xMAINBUF_SIZE = 2x1940 = 3880bytes
static  MP3FrameInfo mp3FrameInfo;      // Content is the output from MP3GetLastFrameInfo, 
                                // we only read this once, and conclude it will be the same in all frames
                                // Maybe this needs to be changed, for different requirements.
static HMP3Decoder hMP3Decoder = 0;    // Content is the pointers to all buffers and information for the MP3 Library
static volatile int bytesLeft = 0;     // Saves how many bytes left in readbuf
static volatile uint32_t outOfData = 0;// Used to set when out of data to quit playback


static uint8_t readBuf[READBUF_SIZE];   // Read buffer where data from SD card is read to
static uint8_t *readPtr = 0;            // Pointer to the next new data
static int32_t offset = 0;              // Used to save the offset to the next frame    
static int errs = 0;                    // Return value from MP3decoder

#define OUT_BUFF_SIZE 2304
static short OutData[OUT_BUFF_SIZE * 2] ={ 0 };
static volatile uint8_t c_play = 0, c_buff = 0, isplay = 0;

void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	c_play = 0;
}

void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
	c_play = 1;
}

static uint32_t len = 0;
static FIL Mp3File;
static uint32_t res = 0;

static uint32_t file_size = 0;
static uint32_t have_read_size = 0;
static uint32_t total_time = 0;

int mp3play(char* filename)
{
	uint32_t head_size = 0;

	if(hMP3Decoder == 0)
	{
		hMP3Decoder = MP3InitDecoder();
	}
	
	bytesLeft = 0;
	readPtr = readBuf;	
	
	if(f_open(&Mp3File, (const TCHAR*)filename, FA_READ)!= FR_OK)
	{
		printf("MP3 Open File [%s] Fail...\r\n", filename);
		return -1;
	}
		
	file_size = f_size(&Mp3File);
	printf("MP3 File Size: %d \r\n", file_size);

	res = f_read(&Mp3File, readBuf, 10, &len);
	printf("Read rer:%d,len: %d \r\n", res, len);
	
	if(readBuf[0] == 'I' && readBuf[1] == 'D' && readBuf[2] == '3')
	{
		head_size |= readBuf[6]; head_size <<= 7;
		head_size |= readBuf[7]; head_size <<= 7;
		head_size |= readBuf[8]; head_size <<= 7;
		head_size |= readBuf[9]; 
		printf("MP3 ID3V2 Size :%d \r\n", head_size);
		
		file_size -= (10 + head_size); // mp3 data size
		res = f_lseek(&Mp3File, 10 + head_size);
		printf("Read seek res:%d,len: %d \r\n", res, head_size);
		
		res = f_read(&Mp3File, readBuf, READBUF_SIZE, &len);
		printf("Read res:%d,len: %d \r\n", res, len);
	}
	else
	{
		printf("MP3 no ID3V2, %02x %02x %02x \r\n", readBuf[0], readBuf[1], readBuf[2]);
		res = f_read(&Mp3File, readBuf + 10, READBUF_SIZE - 10, &len);
		printf("Read res:%d,len: %d \r\n", res, len);
	}
	
	have_read_size = len;
	
	bytesLeft = len;
	
	offset = MP3FindSyncWord(readPtr, bytesLeft);
	if (offset < 0) 
	{
		printf("MP3 Decode Fail!\r\n");
		f_close(&Mp3File);
		return -2;
	}

	readPtr += offset;       //data start point
	bytesLeft -= offset;     //in buffer
			
	errs = MP3Decode(hMP3Decoder, &readPtr, (int*)&bytesLeft, OutData, 0);
	MP3GetLastFrameInfo(hMP3Decoder, &mp3FrameInfo);		
			
	c_buff ++;
	c_buff &= 0x01;

	if(errs != 0)
	{
		printf("MP3 Decode Error No:%d\r\n",errs);
		f_close(&Mp3File);
		return -3;
	}
	
	printf("bitrate:%d\r\n", mp3FrameInfo.bitrate);
	printf("bitsPerSample:%d\r\n", mp3FrameInfo.bitsPerSample);
	printf("outputSamps:%d\r\n", mp3FrameInfo.outputSamps);
	printf("samprate:%d\r\n", mp3FrameInfo.samprate);
	
	total_time = mp3FrameInfo.bitrate / 8;
	total_time = file_size / total_time; // time in S
	
	hi2s2.Init.AudioFreq = mp3FrameInfo.samprate;
	HAL_I2S_Init(&hi2s2);
	
	isplay = 1;

	HAL_I2S_Transmit_DMA(&hi2s2, (unsigned short*)OutData, OUT_BUFF_SIZE * 2);

	return 0;
}

void mp3pause()
{
	if(isplay == 0) return;
	isplay = 2;
	HAL_I2S_DMAPause(&hi2s2);
	printf("MP3 Pause\r\n");
}

void mp3resume()
{
	if(isplay == 0) return;
	isplay = 1;
	HAL_I2S_DMAResume(&hi2s2);
	printf("MP3 Resume\r\n");
}
int mp3state()
{
	return isplay;
}

void mp3stop()
{
	if(isplay == 0) return;
	isplay = 0;
	f_close(&Mp3File);
	HAL_I2S_DMAStop(&hi2s2);
	printf("MP3 Stop\r\n");
}

int mp3get_total_time(char *p)
{
	if(p != NULL)sprintf(p, "%02d:%02d", total_time/60, total_time%60);
	return total_time;
}

int mp3get_play_time(char *p)
{
	int a = mp3FrameInfo.bitrate / 8;
	a = have_read_size / a; // time in S
	if(p != NULL)sprintf(p, "%02d:%02d", a/60, a%60);
	return a;
}

int mp3get_play_progress(void)
{
	double a = (double)have_read_size / file_size;
	a = a * 116;
	return (int)a;
}

void mp3_play_loop()
{
	if (isplay == 0) return;
	if (c_buff != c_play)
	{
		offset = MP3FindSyncWord(readPtr, bytesLeft);
		if (offset < 0) 
		{
			isplay = 0;
			f_close(&Mp3File);
			HAL_I2S_DMAStop(&hi2s2);
			printf("decode Fail!\r\n");
		}
		else
		{
			readPtr += offset;                         //data start point
			bytesLeft -= offset;                 //in buffer
			
			errs = MP3Decode(hMP3Decoder, &readPtr, (int*)&bytesLeft, OutData + c_buff * OUT_BUFF_SIZE, 0);
			MP3GetLastFrameInfo(hMP3Decoder, &mp3FrameInfo);		
			
			c_buff ++;
			c_buff &= 0x01;
		}
	}
	if (bytesLeft < READBUF_SIZE/2)
	{
		memmove(readBuf, readPtr, bytesLeft);
		res = f_read(&Mp3File, readBuf + bytesLeft, READBUF_SIZE - bytesLeft, &len);
		printf("Read res:%d,len: %d \r\n", res, len);
		have_read_size += len;
		if( (res) ||(len == 0)) 
		{
			//break; //read fial or file end;
			isplay = 0;
			f_close(&Mp3File);
			HAL_I2S_DMAStop(&hi2s2);
			printf("Mp3 Play End\r\n");
		}
		
		if (len < READBUF_SIZE - bytesLeft)
		{
			memset(readBuf + bytesLeft + len, 0, READBUF_SIZE - bytesLeft - len);
		}
		bytesLeft=READBUF_SIZE;
		readPtr=readBuf;       
	}
}

